package com.gf.framework.web.config;

import com.gf.framework.common.result.R;
import com.gf.framework.common.utils.SpringContextUtil;
import com.gf.framework.web.excel.IExportExcel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;

import javax.servlet.ServletResponse;
import java.util.*;

/**
* @Author: gaofei
* @Date: 2022/5/22
* @Description: 返回值装饰器
*/
@Configuration
@Slf4j
public class InitializingAdviceDecorator implements InitializingBean, SmartInitializingSingleton {

    private static final Set<String> contentTypeSet = new HashSet<>();

    @Value("${spring.auto-returnpack:false}")
    private Boolean autoReturnPack;

    private final RequestMappingHandlerAdapter adapter;

    public InitializingAdviceDecorator(RequestMappingHandlerAdapter adapter) {
        this.adapter = adapter;
    }

    @Override
    public void afterPropertiesSet() {
        //获取所有的handler对象
        List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();
        //因为上面返回的是unmodifiableList，所以需要新建list处理
        assert returnValueHandlers != null;

        List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(returnValueHandlers);
        this.decorateHandlers(handlers);
        //将增强的返回值回写回去
        adapter.setReturnValueHandlers(handlers);
    }


    /**
    * @Author: gaofei
    * @Date: 2022/5/22
    * @Description: 自定义的返回值控制类
    */
    private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {
        for (HandlerMethodReturnValueHandler handler : handlers) {
            if (handler instanceof RequestResponseBodyMethodProcessor) {
                //找到返回值的handler并将起包装成自定义的handler
                ControllerReturnValueHandler decorator = new ControllerReturnValueHandler((RequestResponseBodyMethodProcessor) handler, autoReturnPack);
                int index = handlers.indexOf(handler);
                handlers.set(index, decorator);
                break;
            }
        }
    }

    @Override
    public void afterSingletonsInstantiated() {
        Map<String, IExportExcel> iExportExcelMap = SpringContextUtil.getContext().getBeansOfType(IExportExcel.class);
        iExportExcelMap.values().forEach(iExportExcel -> contentTypeSet.add(iExportExcel.getContentType()));
    }

    /**
     * 自定义返回值的Handler
     * 采用装饰者模式
     */
    private static class ControllerReturnValueHandler implements HandlerMethodReturnValueHandler {
        //持有一个被装饰者对象
        private HandlerMethodReturnValueHandler handler;
        private Boolean autoReturnPack;

        ControllerReturnValueHandler(RequestResponseBodyMethodProcessor handler, boolean autoReturnPack) {
            this.handler = handler;
            this.autoReturnPack = autoReturnPack;
        }

        @Override
        public boolean supportsReturnType(MethodParameter returnType) {
            return true;
        }

        /**
         * 增强被装饰者的功能
         *
         * @param returnValue  返回值
         * @param returnType   返回类型
         * @param mavContainer view
         * @param webRequest   请求对象
         * @throws Exception 抛出异常
         */
        @Override
        public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
            //如果是下载文件跳过包装
            if (returnType.getParameterType().equals(Void.class)) {
                handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
                return;
            }

            Optional<String> contentType = Optional.of(webRequest)
                    .map(nativeWebRequest -> ((ServletWebRequest) webRequest))
                    .map(ServletRequestAttributes::getResponse)
                    .map(ServletResponse::getContentType);
            if (contentType.isPresent()) {
                for (String contentTypeTemp : contentTypeSet) {
                    if (contentType.get().contains(contentTypeTemp)) {
                        mavContainer.setRequestHandled(true);
                        return;
                    }
                }
            }
            if (autoReturnPack) {
                //如果已经封装了结构体就直接放行
                if (returnValue instanceof R) {
                    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
                    return;
                }
                //正常返回success
                R<Object> success = R.success(returnValue);
                handler.handleReturnValue(success, returnType, mavContainer, webRequest);
            }
            else {
                handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
            }
        }
    }
}